Function.RegisterNamespace("Test.xUnit.js.Console");

[Fixture]
Test.xUnit.js.Console.Runner=function(){
    [Fixture]
    function Runner(){
        [Fact]
        function InstantiatesConsoleScriptLoader(){
            var targetInstance=xUnit.js.Attributes.Engine.Instance;
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(){}
                }
            });
            var mockLoader=Mocks.GetMock(xUnit.js.Console,"ScriptLoader",function(){
                this.Events={Add:function(){}};

                actual=true;
            });
            var actual=false;

            mockEngine(function(){
                mockLoader(function(){
                    new xUnit.js.Console.Runner();
                });
            });
                    
            Assert.True(actual);
        }

        [Fact]
        function AddsAfterRunEventOnAttributeEngine(){
            var targetInstance=xUnit.js.Attributes.Engine.Instance;
            var targetCalls=0;
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(type,handler){
                        if(++targetCalls==1)actual=type;
                    }
                }
            });
            var mockEnvironment=Mocks.GetMock(System,"Environment",getStubEnvironment());
            var actual=null;
            var expected="AfterRun";
            
            mockEnvironment(function(){
                mockEngine(function(){
                    new xUnit.js.Console.Runner();
                })
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function AddsBeforeRunEventOnAttributeEngine(){
            var targetInstance=xUnit.js.Attributes.Engine.Instance;
            var targetCalls=0;
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(type,handler){
                        if(++targetCalls==2)actual=type;
                    }
                }
            });
            var mockEnvironment=Mocks.GetMock(System,"Environment",getStubEnvironment());
            var actual=null;
            var expected="BeforeRun";
            
            mockEnvironment(function(){
                mockEngine(function(){
                    new xUnit.js.Console.Runner();
                })
            });
            
            Assert.Equal(expected,actual);
        }
    }
    
    [Fixture]
    function Run(){
        [Fact]
        function CallsEnvironmentGetParameters(){
            var mockLoader=Mocks.GetMock(xUnit.js.Console,"ScriptLoader",function(){
                this.Events={Add:function(){}};
                this.GetScriptList=function(){return []};
                this.LoadScripts=function(){};
            });
            var mockEnvironment=Mocks.GetMock(System,"Environment",getStubEnvironment());
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(){}
                },
                Run:function(){}
            });
            var actual=false;
            
            mockEnvironment(function(){
                mockLoader(function(){
                    mockEngine(function(){
                        var target=new xUnit.js.Console.Runner();
                        target.Run();
                        actual=System.Environment.GetParameters.Calls.length==1;
                    });
                });
            });
            
            Assert.True(actual);
        }
                
        [Fact]
        function CallsEnvironmentGetWorkingDirectory(){
            var mockEnvironment=Mocks.GetMock(System,"Environment",getStubEnvironment());
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(){}
                },
                Run:function(){}
            });
            var actual=false;

            mockEnvironment(function(){
                mockEngine(function(){
                    var target=new xUnit.js.Console.Runner();
                    target.Run();
                    actual=System.Environment.GetWorkingDirectory.Calls.length>=1;
                });
            });
            
            Assert.True(actual);
        }

        [Fact]
        function CallsPathSetRoot(){
            var mockSetRoot=Mocks.GetMock(System.IO.Path,"SetRoot",function(){
                actual=true;
            });
            var mockEnvironment=Mocks.GetMock(System,"Environment",getStubEnvironment());
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(){}
                },
                Run:function(){}
            });
            var actual=false;

            mockEnvironment(function(){
                mockSetRoot(function(){
                    mockEngine(function(){
                        var target=new xUnit.js.Console.Runner();
                        target.Run();
                    });
                });
            });
            
            Assert.True(actual);
        }

        [Fact]
        function PassesWorkingDirectoryToPathSetRoot(){
            var actual=null;
            var expected="expected";
            var mockSetRoot=Mocks.GetMock(System.IO.Path,"SetRoot",function(path){
                actual=path;
            });
            var targetEnvironment=getStubEnvironment();
            targetEnvironment.GetWorkingDirectory.ReturnValue=expected;
            var mockEnvironment=Mocks.GetMock(System,"Environment",targetEnvironment);
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(){}
                },
                Run:function(){}
            });

            mockEnvironment(function(){
                mockSetRoot(function(){
                    mockEngine(function(){
                        var target=new xUnit.js.Console.Runner();
                        target.Run();
                    });
                });
            });

            Assert.Equal(expected,actual);
        }

        [Fact]
        function CallsGetScriptList(){
            var mockLoader=Mocks.GetMock(xUnit.js.Console,"ScriptLoader",function(){
                this.Events={Add:function(){}};
                this.GetScriptList=function(){
                    actual=true;
                    return [];
                };
                this.LoadScripts=function(){};
            });
            var mockEnvironment=Mocks.GetMock(System,"Environment",getStubEnvironment());
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(){}
                },
                Run:function(){}
            });
            var actual=false;

            mockEnvironment(function(){
                mockLoader(function(){
                    mockEngine(function(){
                        var target=new xUnit.js.Console.Runner();
                        target.Run();
                    });
                });
            });
            
            Assert.True(actual);
        }

        [Fact]
        function PassesUnnamedParametersToGetScriptList(){
            var actual=null;
            var expected=["expected"];
            var mockLoader=Mocks.GetMock(xUnit.js.Console,"ScriptLoader",function(){
                this.Events={Add:function(){}};
                this.GetScriptList=function(scriptList){
                    actual=scriptList;
                    return [];
                };
                this.LoadScripts=function(){};
            });
            var targetEnvironment=getStubEnvironment();
            targetEnvironment.GetParameters.ReturnValue.unnamed=expected;
            var mockEnvironment=Mocks.GetMock(System,"Environment",targetEnvironment);
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(){}
                },
                Run:function(){}
            });

            mockEnvironment(function(){
                mockLoader(function(){
                    mockEngine(function(){
                        var target=new xUnit.js.Console.Runner();
                        target.Run();
                    });
                });
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function PassesNamedExtensionsToGetScriptList(){
            var actual=null;
            var expected="expected";
            var mockLoader=Mocks.GetMock(xUnit.js.Console,"ScriptLoader",function(){
                this.Events={Add:function(){}};
                this.GetScriptList=function(scriptList,extensions){
                    actual=extensions;
                    return [];
                };
                this.LoadScripts=function(){};
            });
            var targetEnvironment=getStubEnvironment();
            targetEnvironment.GetParameters.ReturnValue.named.extensions=expected;
            var mockEnvironment=Mocks.GetMock(System,"Environment",targetEnvironment);
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(){}
                },
                Run:function(){}
            });
            
            mockEnvironment(function(){
                mockLoader(function(){
                    mockEngine(function(){
                        var target=new xUnit.js.Console.Runner();
                        target.Run();
                    });
                });
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function CallsLoadScripts(){
            var mockLoader=Mocks.GetMock(xUnit.js.Console,"ScriptLoader",function(){
                this.Events={Add:function(){}};
                this.GetScriptList=function(){
                    return [];
                };
                this.LoadScripts=function(){
                    actual=true;
                };
            });
            var mockEnvironment=Mocks.GetMock(System,"Environment",getStubEnvironment());
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(){}
                },
                Run:function(){}
            });
            var actual=false;
            
            mockEnvironment(function(){
                mockLoader(function(){
                    mockEngine(function(){
                        var target=new xUnit.js.Console.Runner();
                        target.Run();
                    });
                });
            });

            Assert.True(actual);
        }

        [Fact]
        function PassesResultOfGetScriptListToLoadScripts(){
            var mockLoader=Mocks.GetMock(xUnit.js.Console,"ScriptLoader",function(){
                this.Events={Add:function(){}};
                this.GetScriptList=function(){
                    return expected;
                };
                this.LoadScripts=function(scriptList){
                    actual=scriptList;
                };
            });
            var mockEnvironment=Mocks.GetMock(System,"Environment",getStubEnvironment());
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(){}
                },
                Run:function(){}
            });
            var expected=["expected"];
            var actual=null;
            
            mockEnvironment(function(){
                mockLoader(function(){
                    mockEngine(function(){
                        var target=new xUnit.js.Console.Runner();
                        target.Run();
                    });
                });
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function CallsEnumerateOnAttributeEngineInstance(){
            var mockLoader=Mocks.GetMock(xUnit.js.Console,"ScriptLoader",function(){
                this.Events={Add:function(){}};
                this.GetScriptList=function(){
                    return [];
                };
                this.LoadScripts=function(){};
            });
            var targetEnvironment=getStubEnvironment();
            targetEnvironment.GetParameters.ReturnValue.named.action='enumerate';
            var mockEnvironment=Mocks.GetMock(System,"Environment",targetEnvironment);
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(){}
                },
                Enumerate:function(){
                    actual=true;
                    return [];
                }                
            });
            var actual=false;
            
            mockEnvironment(function(){
                mockLoader(function(){
                    mockEngine(function(){
                        var target=new xUnit.js.Console.Runner();
                        target.Run();
                    });
                });
            });

            Assert.True(actual);
        }

        [Fact]
        function PassesTargetToEnumerateOnAttributeEngineInstance(){
            var actual=null;
            var expected="expected";
            var mockLoader=Mocks.GetMock(xUnit.js.Console,"ScriptLoader",function(){
                this.Events={Add:function(){}};
                this.GetScriptList=function(){
                    return [];
                };
                this.LoadScripts=function(){};
            });
            var targetEnvironment=getStubEnvironment();
            targetEnvironment.GetParameters.ReturnValue.named={action:'enumerate',target:expected};
            var mockEnvironment=Mocks.GetMock(System,"Environment",targetEnvironment);
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(){}
                },
                Enumerate:function(target){
                    actual=target;
                    return [];
                }                
            });

            mockEnvironment(function(){
                mockLoader(function(){
                    mockEngine(function(){
                        var target=new xUnit.js.Console.Runner();
                        target.Run();
                    });
                });
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function CallsRunOnAttributeEngineInstance(){
            var mockLoader=Mocks.GetMock(xUnit.js.Console,"ScriptLoader",function(){
                this.Events={Add:function(){}};
                this.GetScriptList=function(){
                    return [];
                };
                this.LoadScripts=function(){};
            });
            var mockEnvironment=Mocks.GetMock(System,"Environment",getStubEnvironment());
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(){}
                },
                Run:function(target){
                    actual=true;
                }                
            });
            var actual=false;

            mockEnvironment(function(){
                mockLoader(function(){
                    mockEngine(function(){
                        var target=new xUnit.js.Console.Runner();
                        target.Run();
                    });
                });
            });
            
            Assert.True(actual);
        }

        [Fact]
        function PassesTargetToRunOnAttributeEngineInstance(){
            var actual=null;
            var expected="expected";
            var mockLoader=Mocks.GetMock(xUnit.js.Console,"ScriptLoader",function(){
                this.Events={Add:function(){}};
                this.GetScriptList=function(){
                    return [];
                };
                this.LoadScripts=function(){};
            });
            var targetEnvironment=getStubEnvironment();
            targetEnvironment.GetParameters.ReturnValue.named.target=expected;
            var mockEnvironment=Mocks.GetMock(System,"Environment",targetEnvironment);
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(){}
                },
                Run:function(target){
                    actual=target;
                }                
            });

            mockEnvironment(function(){
                mockLoader(function(){
                    mockEngine(function(){
                        var target=new xUnit.js.Console.Runner();
                        target.Run();
                    });
                });
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function CallsUsageIfNoUnnamedParametersArePasssed(){
            var stubEnvironment=getStubEnvironment();
            stubEnvironment.GetParameters.ReturnValue.unnamed.length=0;
            var mockEnvironment=Mocks.GetMock(System,"Environment",stubEnvironment);
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(){}
                }
            });
            var actual=null;
            var expected=[
                "",
                "xUnit.js Console Runner v0.8.3.9",
                "",
                "Usage:",
                "<environment> xUnit.js.Console.js [/action:<action>] [/extensions:<extensions>] [/output:<output>] [/strict:<strict>] [/target:<name>] [/-target:<name>] [/trait:<name>] [/-trait:<name>] [/verbose:<verbose>] [/dependency:<dependency>] <path>[ <path>...] ",
                "",
                "<environment> The environment in which the tests are to be run.",
                "",
                "              Supported environments include:",
                "",
                "                - Microsoft Script Engine (cscript.exe, Windows only)",
                "                - Google's V8 developer console (D8 executable)",
                "                - Mozilla's SpiderMonkey console (js executable)",
                "                - Mozilla's Rhino console (js.jar)",
                "",
                "<action>      The action to perform. Optional. Valid actions are ",
                "              [Enumerate|RunTests]. If omitted, defaults to 'RunTests'.",
                "",
                "<extensions>  A comma separated list of file extensions to include while",
                "              searching <path> for files to load. If omitted, defaults ", 
                "              to '.js'.",
                "",
                "<output>      The desired output type. Optional. Valid outputs are ",
                "              [Text|Xml|Json]. If omitted, defaults to 'Text'.",
                "",
                "<strict>      Toggles strict mode on or off. Valid options are [true|false].",
                "              Optional. If 'true', fails files and facts that violate ",
                "              Single-Assert or pollute the global namespace. If 'false', allows",
                "              multiple asserts per fact, and ignores global variables set in",
                "              files or facts. If omitted, defaults to 'true'.",
                "",
                "<target>      The name of a fact, fixture, or partial namespace to run. ",
                "              Optional. If omitted, runs all facts and fixtures found and ",
                "              registered at <path>.",
                "",
                "<trait>       The name, or comma separated list of names, of traits to run. ",
                "              Optional. If omitted, runs all facts and fixtures found and ",
                "              registered at <path>. If specified, runs only targets designated ",
                "              with the matching trait attribute, e.g. '[Trait(\"trait\")]'.",
                "",
                "<-trait>      The name, or comma separated list of names, of traits to skip. ",
                "              Optional. If omitted, runs all facts and fixtures found and ",
                "              registered at <path>. If specified, runs only targets not ",
                "              designated with a matching trait attribute.",
                "",
                "<verbose>     Toggles verbose mode on or off. Valid options are [true|false].",
                "              Optional. If 'true', includes additional information in output,",
                "              such as stack traces and file names. Defaults to false.",
                "",
                "<dependency>  The path or comma separated list of paths to a script file or ",
                "              directory containing dependencies to load before beginning the ",
                "              test run. These files will load before any files in <path>,",
                "              and before any output is written.",
                "",
                "<path>        The path or space separated list of paths to a script file or ",
                "              directory containing files to load and parse for tests.",
                "",
                "",
                "Examples:",
                "",
                "  cscript xUnit.js.Console.js /action:Enumerate xUnit.js/Tests",
                "",
                "  d8 - xUnit.js.Console.js -- /target:Test.xUnit.js.Console xUnit.js/Tests",
                "",
                "  js xUnit.js.Console.js xUnit.js/Tests/xUnit.js.Console/Runner.js",
                "",
                "  java -jar js.jar xUnit.js.Console.js /output:Xml xUnit.js/Tests",
                "",
                ""
            ].join('\n');

            mockEnvironment(function(){
                mockEngine(function(){
                    var target=new xUnit.js.Console.Runner();
                    target.Run();
                    actual=System.Environment.Write.Calls[0].Arguments.message;
                });
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function CallsEnvironmentExitOnComplete(){
            var mockEnvironment=Mocks.GetMock(System,"Environment",getStubEnvironment());
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",{
                Events:{
                    Add:function(){}
                },
                Run:function(){}
            });
            var actual=false;

            mockEnvironment(function(){
                mockEngine(function(){
                    new xUnit.js.Console.Runner().Run();
                    actual=System.Environment.Exit.Calls.length==1;
                });
            });
            
            Assert.True(actual);
        }

        [Fact]
        function PassesErrorCodeToEnvironmentExitOnCompleteForFailedFact(){
            var mockEnvironment=Mocks.GetMock(System,"Environment",getStubEnvironment());
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",new xUnit.js.Attributes.Engine());
            var mockFileIO=Mocks.GetMock(System.IO.File,"Exists",function(){return true;});
            var actual=null;
            var expected=0xFAE1;

            mockEnvironment(function(){
                mockEngine(function(){
                    mockFileIO(function(){
                        xUnit.js.Attributes.Engine.Instance.RegisterFact(function(){Assert.Fail();});
                        new xUnit.js.Console.Runner().Run();
                        Assert.Calls--;
                        actual=System.Environment.Exit.Calls[0].Arguments.errorCode;
                    });
                });
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function PassesErrorCodeToEnvironmentExitOnCompleteForErroredFact(){
            var mockEnvironment=Mocks.GetMock(System,"Environment",getStubEnvironment());
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",new xUnit.js.Attributes.Engine());
            var mockFileIO=Mocks.GetMock(System.IO.File,"Exists",function(){return true;});
            var actual=null;
            var expected=0xBAD;

            mockEnvironment(function(){
                mockEngine(function(){
                    mockFileIO(function(){
                        xUnit.js.Attributes.Engine.Instance.RegisterFact(function(){throw new Error()});
                        new xUnit.js.Console.Runner().Run();
                        actual=System.Environment.Exit.Calls[0].Arguments.errorCode;
                    });
                });
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function PassesErrorCodeToEnvironmentExitOnCompleteForSuccessFact(){
            var mockEnvironment=Mocks.GetMock(System,"Environment",getStubEnvironment());
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",new xUnit.js.Attributes.Engine());
            var mockFileIO=Mocks.GetMock(System.IO.File,"Exists",function(){return true;});
            var actual=null;
            var expected=0;

            mockEnvironment(function(){
                mockEngine(function(){
                    mockFileIO(function(){
                        xUnit.js.Attributes.Engine.Instance.RegisterFact(function(){Assert.True(true)});
                        new xUnit.js.Console.Runner().Run();
                        Assert.Calls--;
                        actual=System.Environment.Exit.Calls[0].Arguments.errorCode;
                    });
                });
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function PassesErrorCodeToEnvironmentExitOnCompleteForSkippedFact(){
            var mockEnvironment=Mocks.GetMock(System,"Environment",getStubEnvironment());
            var mockEngine=Mocks.GetMock(xUnit.js.Attributes.Engine,"Instance",new xUnit.js.Attributes.Engine());
            var mockFileIO=Mocks.GetMock(System.IO.File,"Exists",function(){return true;});
            var actual=null;
            var expected=0;
            var target=function targetMethod(){};

            mockEnvironment(function(){
                mockEngine(function(){
                    mockFileIO(function(){
                        xUnit.js.Attributes.Engine.Instance.RegisterFact(target);
                        var targetFact=xUnit.js.Attributes.Engine.Instance.Get().GetFacts()[0];
                        targetFact.Skip();
                        var runner=new xUnit.js.Console.Runner();
                        runner.Run();
                        actual=System.Environment.Exit.Calls[0].Arguments.errorCode;
                    });
                });
            });
            
            Assert.Equal(expected,actual);
        }

    }
    // Stubs
    function getStubEnvironment(){
        return Stubs.GetObject({
            GetNewLine:{returnValue:"\n"},
            GetParameters:{returnValue:{named:{},unnamed:['']}},
            GetWorkingDirectory:{returnValue:''},
            Exit:{parameters:["errorCode"]},
            Write:{parameters:["message"]},
            WriteError:{parameters:["message"]}
        });
    }
}